/************************ (C) COPYRIGHT Megahuntmicro *************************
 * File Name            : usbh_core.c 
 * Author               : Megahuntmicro
 * Version              : V1.0.0
 * Date                 : 21-October-2014
 * Description          : This file implements the functions for the core state machine process
  *                       the enumeration and the control transfer process.
 *****************************************************************************/
 
/* Include ------------------------------------------------------------------*/
#include "usbh_ioreq.h"
#include "usb_bsp.h"
#include "usbh_hcs.h"
#include "usbh_stdreq.h"
#include "usbh_core.h"
#include "usb_hcd_int.h"
/* Private typedef ----------------------------------------------------------*/
/* Private define -----------------------------------------------------------*/	
/* Private macro ------------------------------------------------------------*/	
/* Private variables --------------------------------------------------------*/	
/* Ptivate function prototypes ----------------------------------------------*/	


/** @addtogroup USBH_LIB
  * @{
  */

/** @addtogroup USBH_LIB_CORE
* @{
*/

/** @defgroup USBH_CORE 
  * @brief TThis file handles the basic enumaration when a device is connected 
  *          to the host.
  * @{
  */ 

/** @defgroup USBH_CORE_Private_TypesDefinitions
  * @{
  */ 
uint8_t USBH_Disconnected (USB_OTG_CORE_HANDLE *pdev); 
uint8_t USBH_Connected (USB_OTG_CORE_HANDLE *pdev); 
uint8_t USBH_SOF (USB_OTG_CORE_HANDLE *pdev); 
uint8_t USBH_VbusError (USB_OTG_CORE_HANDLE *pdev);
uint8_t USBH_SessReq (USB_OTG_CORE_HANDLE *pdev);
uint8_t USBH_Babble (USB_OTG_CORE_HANDLE *pdev);
uint8_t USBH_Resume (USB_OTG_CORE_HANDLE *pdev);

USBH_HCD_INT_cb_TypeDef USBH_HCD_INT_cb = 
{
    USBH_Resume,
    USBH_Babble,
    USBH_SessReq,
    USBH_VbusError,
    USBH_SOF,
    USBH_Connected, 
    USBH_Disconnected,    
};

USBH_HCD_INT_cb_TypeDef  *USBH_HCD_INT_fops = &USBH_HCD_INT_cb;
/**
  * @}
  */ 


/** @defgroup USBH_CORE_Private_Defines
  * @{
  */ 
/**
  * @}
  */ 


/** @defgroup USBH_CORE_Private_Macros
  * @{
  */ 
/**
  * @}
  */ 


/** @defgroup USBH_CORE_Private_Variables
  * @{
  */ 
/**
  * @}
  */ 


/** @defgroup USBH_CORE_Private_FunctionPrototypes
  * @{
  */
static USBH_Status USBH_HandleEnum(USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost);
USBH_Status USBH_HandleControl (USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost);

/**
  * @}
  */ 


/** @defgroup USBH_CORE_Private_Functions
  * @{
  */ 


/**
  * @brief  USBH_Connected
  *         USB Connect callback function from the Interrupt. 
  * @param  selected device
  * @retval Status
*/
uint8_t USBH_Connected (USB_OTG_CORE_HANDLE *pdev)
{
    pdev->host.ConnSts = 1;
    return 0;
}

/**
* @brief  USBH_Disconnected
*         USB Disconnect callback function from the Interrupt. 
* @param  selected device
* @retval Status
*/

uint8_t USBH_Disconnected (USB_OTG_CORE_HANDLE *pdev)
{
    pdev->host.ConnSts = 0;
    return 0;  
}

/**
  * @brief  USBH_SOF
  *         USB SOF callback function from the Interrupt. 
  * @param  selected device
  * @retval Status
  */

uint8_t USBH_SOF (USB_OTG_CORE_HANDLE *pdev)
{
    /* This callback could be used to implement a scheduler process */
    return 0;  
}
/**
  * @brief  USBH_VbusError
  *         USB VBus Error callback function from the Interrupt. 
  * @param  selected device
  * @retval Status
  */

uint8_t USBH_VbusError (USB_OTG_CORE_HANDLE *pdev)
{
    /* This callback could be used to implement a scheduler process */
    return 0;  
}
/**
  * @brief  USBH_SessReq
  *         USB SessReq callback function from the Interrupt. 
  * @param  selected device
  * @retval Status
  */

uint8_t USBH_SessReq (USB_OTG_CORE_HANDLE *pdev)
{
    /* This callback could be used to implement a scheduler process */
    return 0;  
}
/**
  * @brief  USBH_Babble
  *         USB Babble callback function from the Interrupt. 
  * @param  selected device
  * @retval Status
  */

uint8_t USBH_Babble (USB_OTG_CORE_HANDLE *pdev)
{
    /* This callback could be used to implement a scheduler process */
    return 0;  
}
/**
  * @brief  USBH_Resume
  *         USB Resume callback function from the Interrupt. 
  * @param  selected device
  * @retval Status
  */

uint8_t USBH_Resume (USB_OTG_CORE_HANDLE *pdev)
{
    /* This callback could be used to implement a scheduler process */
    return 0;  
}
/**
  * @brief  USBH_Init
  *         Host hardware and stack initializations 
  * @param  class_cb: Class callback structure address
  * @param  usr_cb: User callback structure address
  * @retval None
  */
void USBH_Init(USB_OTG_CORE_HANDLE *pdev,
               USB_OTG_CORE_ID_TypeDef coreID,
               USBH_HOST *phost,               
               USBH_Class_cb_TypeDef *class_cb, 
               USBH_Usr_cb_TypeDef *usr_cb)
{
     
    /* Hardware Init */
    USB_OTG_BSP_Init(pdev);  

    /* configure GPIO pin used for switching VBUS power */
    USB_OTG_BSP_ConfigVBUS(NULL);  

    /* Host de-initializations */
    USBH_DeInit(pdev, phost);

    /*Register class and user callbacks */
    phost->class_cb = class_cb;
    phost->usr_cb = usr_cb;  

    /* Start the USB OTG core */     
    HCD_Init(pdev , coreID);

    /* Upon Init call usr call back */
    phost->usr_cb->Init();

    /* Enable Interrupts */
    USB_OTG_BSP_EnableInterrupt(pdev);
}

/**
  * @brief  USBH_DeInit 
  *         Re-Initialize Host
  * @param  None 
  * @retval status: USBH_Status
  */
USBH_Status USBH_DeInit(USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost)
{
    /* Software Init */
    phost->gState = HOST_IDLE;
    phost->gStateBkp = HOST_IDLE; 
    phost->EnumState = ENUM_IDLE;
    phost->RequestState = CMD_SEND;  

    phost->Control.state = CTRL_SETUP;
    phost->Control.ep0size = USB_OTG_MAX_EP0_SIZE;  

    phost->device_prop.address = USBH_DEVICE_ADDRESS_DEFAULT;
    phost->device_prop.speed = HPRT0_PRTSPD_FULL_SPEED;

    USBH_Free_Channel(pdev, phost->Control.hc_num_in);
    USBH_Free_Channel(pdev, phost->Control.hc_num_out);
    
    return USBH_OK;
}

/**
* @brief  USBH_Process
*         USB Host core main state machine process
* @param  None 
* @retval None
*/
void USBH_Process(USB_OTG_CORE_HANDLE *pdev , USBH_HOST *phost)
{
    volatile USBH_Status status = USBH_FAIL;

    /* check for Host port events */
    if ((HCD_IsDeviceConnected(pdev) == 0) && (phost->gState != HOST_IDLE)) 
    {
        if (phost->gState != HOST_DEV_DISCONNECTED) 
        {
            phost->gState = HOST_DEV_DISCONNECTED;
        }
    }
      
    switch (phost->gState)
    {
    case HOST_IDLE :
        if (HCD_IsDeviceConnected(pdev))  
        {
            phost->gState = HOST_DEV_ATTACHED;
            USB_OTG_BSP_mDelay(100);
        }
        break;

    case HOST_DEV_ATTACHED :
        phost->usr_cb->DeviceAttached();
        phost->Control.hc_num_out = USBH_Alloc_Channel(pdev, 0x00);
        phost->Control.hc_num_in = USBH_Alloc_Channel(pdev, 0x80);  

        /* Reset USB Device */
        if (HCD_ResetPort(pdev) == 0)
        {
            phost->usr_cb->ResetDevice();
            /*  Wait for USB USBH_ISR_PrtEnDisableChange()  
            Host is Now ready to start the Enumeration 
            */

            phost->device_prop.speed = HCD_GetCurrentSpeed(pdev);

            phost->gState = HOST_ENUMERATION;
            phost->usr_cb->DeviceSpeedDetected(phost->device_prop.speed);

            /* Open Control pipes */
            USBH_Open_Channel (pdev,
                               phost->Control.hc_num_in,
                               phost->device_prop.address,
                               phost->device_prop.speed,
                               EP_TYPE_CTRL,
                               phost->Control.ep0size); 

            /* Open Control pipes */
            USBH_Open_Channel (pdev,
                               phost->Control.hc_num_out,
                               phost->device_prop.address,
                               phost->device_prop.speed,
                               EP_TYPE_CTRL,
                               phost->Control.ep0size);

            USB_OTG_BSP_mDelay(10);
        }
        break;

    case HOST_ENUMERATION:     
        /* Check for enumeration status */  
        if (USBH_HandleEnum(pdev , phost) == USBH_OK)
        { 
            /* The function shall return USBH_OK when full enumeration is complete */

            /* user callback for end of device basic enumeration */
            phost->usr_cb->EnumerationDone();

            phost->gState  = HOST_USR_INPUT;    
        }
        break;

    case HOST_USR_INPUT:    
        /*The function should return user response true to move to class state */
        if (phost->usr_cb->UserInput() == USBH_USR_RESP_OK)
        {
            if ((phost->class_cb->Init(pdev, phost)) == USBH_OK)
            {
                phost->gState  = HOST_CLASS_REQUEST;     
            }     
        }   
        break;

    case HOST_CLASS_REQUEST:  
        /* process class standard contol requests state machine */ 
        status = phost->class_cb->Requests(pdev, phost);

        if (status == USBH_OK)
        {
            phost->gState  = HOST_CLASS;
        }  
        else
        {
            USBH_ErrorHandle(phost, status);
        }
        break;
        
    case HOST_CLASS:   
        /* process class state machine */
        status = phost->class_cb->Machine(pdev, phost);
        USBH_ErrorHandle(phost, status);
        break;       

    case HOST_CTRL_XFER:
        /* process control transfer state machine */
        USBH_HandleControl(pdev, phost);    
        break;

    case HOST_SUSPENDED:
        break;

    case HOST_ERROR_STATE:
        /* Re-Initilaize Host for new Enumeration */
        USBH_DeInit(pdev, phost);
        phost->usr_cb->DeInit();
        phost->class_cb->DeInit(pdev, &phost->device_prop);
        break;

    case HOST_DEV_DISCONNECTED :
        /* Manage User disconnect operations*/
        phost->usr_cb->DeviceDisconnected();
        /* Re-Initilaize Host for new Enumeration */
        USBH_DeInit(pdev, phost);
        phost->usr_cb->DeInit();
        phost->class_cb->DeInit(pdev, &phost->device_prop); 
        USBH_DeAllocate_AllChannel(pdev);  
        phost->gState = HOST_IDLE;
        break;

    default :
        break;
    }
}

/**
  * @brief  USBH_ErrorHandle 
  *         This function handles the Error on Host side.
  * @param  errType : Type of Error or Busy/OK state
  * @retval None
  */
void USBH_ErrorHandle(USBH_HOST *phost, USBH_Status errType)
{
    /* Error unrecovered or not supported device speed */
    if ((errType == USBH_ERROR_SPEED_UNKNOWN) ||
        (errType == USBH_UNRECOVERED_ERROR))
    {
        //phost->usr_cb->UnrecoveredError(); 
        phost->gState = HOST_ERROR_STATE;   
    }  
    /* USB host restart requested from application layer */
    else if (errType == USBH_APPLY_DEINIT)
    {
        phost->gState = HOST_ERROR_STATE;  
        /* user callback for initalization */
        //phost->usr_cb->Init();
    } 
}

/**
  * @brief  USBH_HandleEnum 
  *         This function includes the complete enumeration process
  * @param  pdev: Selected device
  * @retval USBH_Status
  */
static USBH_Status USBH_HandleEnum(USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost)
{
    USBH_Status Status = USBH_BUSY;  
    uint8_t Local_Buffer[64];

    switch (phost->EnumState)
    {      
    case ENUM_IDLE:  
        /* Get Device Desc for only 1st 8 bytes : To get EP0 MaxPacketSize */
        if (USBH_Get_DevDesc(pdev , phost, 8) == USBH_OK)
        {
            phost->Control.ep0size = phost->device_prop.Dev_Desc.bMaxPacketSize;

            /* Issue Reset  */
            HCD_ResetPort(pdev);
            phost->EnumState = ENUM_GET_FULL_DEV_DESC;

            /* modify control channels configuration for MaxPacket size */
            USBH_Modify_Channel(pdev,
                                phost->Control.hc_num_out,
                                0,
                                0,
                                0,
                                phost->Control.ep0size);

            USBH_Modify_Channel(pdev,
                                phost->Control.hc_num_in,
                                0,
                                0,
                                0,
                                phost->Control.ep0size);
        }
        break;

    case ENUM_GET_FULL_DEV_DESC:      
        /* Get FULL Device Desc  */
        if (USBH_Get_DevDesc(pdev, phost, USB_DEVICE_DESC_SIZE) == USBH_OK)
        {
            /* user callback for device descriptor available */
            phost->usr_cb->DeviceDescAvailable(&phost->device_prop.Dev_Desc);      
            phost->EnumState = ENUM_SET_ADDR;
        }
        break;

    case ENUM_SET_ADDR:    	
        /* set address */
        if (USBH_SetAddress(pdev, phost, USBH_DEVICE_ADDRESS) == USBH_OK)
        {
            USB_OTG_BSP_mDelay(2);
            phost->device_prop.address = USBH_DEVICE_ADDRESS;

            /* user callback for device address assigned */
            phost->usr_cb->DeviceAddressAssigned();
            phost->EnumState = ENUM_GET_CFG_DESC;
            /* modify control channels to update device address */
            USBH_Modify_Channel(pdev,
                                phost->Control.hc_num_in,
                                phost->device_prop.address,
                                0,
                                0,
                                0);

            USBH_Modify_Channel(pdev,
                                phost->Control.hc_num_out,
                                phost->device_prop.address,
                                0,
                                0,
                                0);
        }
        break;

    case ENUM_GET_CFG_DESC: 
        /* get standard configuration descriptor */
        if (USBH_Get_CfgDesc(pdev, phost, USB_CONFIGURATION_DESC_SIZE) == USBH_OK)
        {
            phost->EnumState = ENUM_GET_FULL_CFG_DESC;
        }
        break;

    case ENUM_GET_FULL_CFG_DESC:
        /* get FULL config descriptor (config, interface, endpoints) */
        if (USBH_Get_CfgDesc(pdev, phost, phost->device_prop.Cfg_Desc.wTotalLength) == USBH_OK)
        {
            /* User callback for configuration descriptors available */
            phost->usr_cb->ConfigurationDescAvailable(&phost->device_prop.Cfg_Desc,
                                      phost->device_prop.Itf_Desc,
                                      phost->device_prop.Ep_Desc[0]);

            phost->EnumState = ENUM_GET_MFC_STRING_DESC;
        }
        break;

    case ENUM_GET_MFC_STRING_DESC:
        if (phost->device_prop.Dev_Desc.iManufacturer != 0)   /* Check that Manufacturer String is available */
        {
            if (USBH_Get_StringDesc(pdev,
                                    phost,
                                    phost->device_prop.Dev_Desc.iManufacturer, 
                                    Local_Buffer, 
                                    0xff) == USBH_OK)
            {
                /* User callback for Manufacturing string */
                phost->usr_cb->ManufacturerString(Local_Buffer);
                phost->EnumState = ENUM_GET_PRODUCT_STRING_DESC;
            }
        }
        else
        {
            phost->usr_cb->ManufacturerString("N/A");      
            phost->EnumState = ENUM_GET_PRODUCT_STRING_DESC;
        }
        break;

    case ENUM_GET_PRODUCT_STRING_DESC:   
        if (phost->device_prop.Dev_Desc.iProduct != 0)     /* Check that Product string is available */
        {
            if (USBH_Get_StringDesc(pdev,
                                    phost,
                                    phost->device_prop.Dev_Desc.iProduct, 
                                    Local_Buffer, 
                                    0xff) == USBH_OK)
            {
                /* User callback for Product string */
                phost->usr_cb->ProductString(Local_Buffer);
                phost->EnumState = ENUM_GET_SERIALNUM_STRING_DESC;
            }
        }
        else
        {
            phost->usr_cb->ProductString("N/A");
            phost->EnumState = ENUM_GET_SERIALNUM_STRING_DESC;
        } 
        break;

    case ENUM_GET_SERIALNUM_STRING_DESC:   
        if (phost->device_prop.Dev_Desc.iSerialNumber != 0)   /* Check that Serial number string is available */    
        {
            if (USBH_Get_StringDesc(pdev, 
                                    phost,
                                    phost->device_prop.Dev_Desc.iSerialNumber, 
                                    Local_Buffer, 
                                    0xff) == USBH_OK)
            {
                /* User callback for Serial number string */
                phost->usr_cb->SerialNumString(Local_Buffer);
                phost->EnumState = ENUM_SET_CONFIGURATION;
            }
        }
        else
        {
            phost->usr_cb->SerialNumString("N/A");      
            phost->EnumState = ENUM_SET_CONFIGURATION;
        }  
        break;

    case ENUM_SET_CONFIGURATION:
        /* set configuration  (default config) */
        if (USBH_SetCfg(pdev, phost, phost->device_prop.Cfg_Desc.bConfigurationValue) == USBH_OK)
        {
            phost->EnumState = ENUM_DEV_CONFIGURED;
        }
        break;

    case ENUM_DEV_CONFIGURED:
        /* user callback for enumeration done */
        Status = USBH_OK;
        break;

    default:
        break;
    }  
    
    return Status;
}

/**
  * @brief  USBH_HandleControl
  *         Handles the USB control transfer state machine
  * @param  pdev: Selected device
  * @retval Status
  */
USBH_Status USBH_HandleControl (USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost)
{
    uint8_t direction;  
    static uint16_t timeout = 0;
    USBH_Status status = USBH_OK;
    URB_STATE URB_Status = URB_IDLE;

    phost->Control.status = CTRL_START;

    switch (phost->Control.state)
    {
    case CTRL_SETUP:
        /* send a SETUP packet */
        USBH_CtlSendSetup(pdev, 
                          phost->Control.setup.d8, 
                          phost->Control.hc_num_out);
    
        phost->Control.state = CTRL_SETUP_WAIT;  
        break; 

    case CTRL_SETUP_WAIT:
        URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_out); 
        /* case SETUP packet sent successfully */
        if (URB_Status == URB_DONE)
        { 
            direction = (phost->Control.setup.b.bmRequestType & USB_REQ_DIR_MASK);

            /* check if there is a data stage */
            if (phost->Control.setup.b.wLength.w != 0 )
            {        
                timeout = DATA_STAGE_TIMEOUT;
                if (direction == USB_D2H)
                {
                    /* Data Direction is IN */
                    phost->Control.state = CTRL_DATA_IN;
                }
                else
                {
                    /* Data Direction is OUT */
                    phost->Control.state = CTRL_DATA_OUT;
                } 
            }
            /* No DATA stage */
            else
            {
                timeout = NODATA_STAGE_TIMEOUT;

                /* If there is No Data Transfer Stage */
                if (direction == USB_D2H)
                {
                    /* Data Direction is IN */
                    phost->Control.state = CTRL_STATUS_OUT;
                }
                else
                {
                    /* Data Direction is OUT */
                    phost->Control.state = CTRL_STATUS_IN;
                } 
            }          
            /* Set the delay timer to enable timeout for data stage completion */
            phost->Control.timer = HCD_GetCurrentFrame(pdev);
        }
        else if(URB_Status == URB_ERROR)
        {
            phost->Control.state = CTRL_ERROR;     
            phost->Control.status = CTRL_XACTERR;
        }    
        break;

    case CTRL_DATA_IN:  
        /* Issue an IN token */ 
        USBH_CtlReceiveData(pdev,
                            phost->Control.buff, 
                            phost->Control.length,
                            phost->Control.hc_num_in);

        phost->Control.state = CTRL_DATA_IN_WAIT;
        break;    

    case CTRL_DATA_IN_WAIT:
        URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_in); 

        /* check is DATA packet transfered successfully */
        if (URB_Status == URB_DONE)
        { 
            phost->Control.state = CTRL_STATUS_OUT;
        }

        /* manage error cases*/
        if (URB_Status == URB_STALL) 
        { 
            /* In stall case, return to previous machine state*/
            phost->gState = phost->gStateBkp;
        }   
        else if (URB_Status == URB_ERROR)
        {
            /* Device error */
            phost->Control.state = CTRL_ERROR;    
        }
        else if ((HCD_GetCurrentFrame(pdev)- phost->Control.timer) > timeout)
        {
            /* timeout for IN transfer */
            phost->Control.state = CTRL_ERROR; 
        }   
        break;

    case CTRL_DATA_OUT:
        /* Start DATA out transfer (only one DATA packet)*/
        pdev->host.hc[phost->Control.hc_num_out].toggle_out = 1; 

        USBH_CtlSendData (pdev,
                          phost->Control.buff, 
                          phost->Control.length , 
                          phost->Control.hc_num_out);

        phost->Control.state = CTRL_DATA_OUT_WAIT;
        break;

    case CTRL_DATA_OUT_WAIT:
        URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_out);     
        if (URB_Status == URB_DONE)  /* If the Setup Pkt is sent successful, then change the state */
        {
            phost->Control.state = CTRL_STATUS_IN;
        }
        /* handle error cases */
        else if (URB_Status == URB_STALL) 
        { 
            /* In stall case, return to previous machine state*/
            phost->gState =   phost->gStateBkp;
            phost->Control.state = CTRL_STALLED;  
        } 
        else if (URB_Status == URB_NOTREADY)
        { 
            /* Nack received from device */
            phost->Control.state = CTRL_DATA_OUT;
        }    
        else if (URB_Status == URB_ERROR)
        {
        /* device error */
        phost->Control.state = CTRL_ERROR;      
        } 
        break;

    case CTRL_STATUS_IN:
        /* Send 0 bytes out packet */
        USBH_CtlReceiveData(pdev,
                            0,
                            0,
                            phost->Control.hc_num_in);

        phost->Control.state = CTRL_STATUS_IN_WAIT;
        break;

    case CTRL_STATUS_IN_WAIT:
        URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_in); 
        if (URB_Status == URB_DONE)   /* Control transfers completed, Exit the State Machine */
        {
            phost->gState =   phost->gStateBkp;
            phost->Control.state = CTRL_COMPLETE;
        }
        else if (URB_Status == URB_ERROR)
        {
            phost->Control.state = CTRL_ERROR;  
        }
        else if ((HCD_GetCurrentFrame(pdev) - phost->Control.timer) > timeout)
        {
            phost->Control.state = CTRL_ERROR; 
        }
        else if(URB_Status == URB_STALL)
        {
            /* Control transfers completed, Exit the State Machine */
            phost->gState = phost->gStateBkp;
            phost->Control.status = CTRL_STALL;
            status = USBH_NOT_SUPPORTED;
        }
        break;

    case CTRL_STATUS_OUT:
        pdev->host.hc[phost->Control.hc_num_out].toggle_out ^= 1; 
        USBH_CtlSendData(pdev,
                         0,
                         0,
                         phost->Control.hc_num_out);

        phost->Control.state = CTRL_STATUS_OUT_WAIT;
        break;

    case CTRL_STATUS_OUT_WAIT: 
        URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_out);  
        if (URB_Status == URB_DONE)
        { 
            phost->gState = phost->gStateBkp; 
            phost->Control.state = CTRL_COMPLETE; 
        }
        else if  (URB_Status == URB_NOTREADY)
        { 
            phost->Control.state = CTRL_STATUS_OUT;
        }      
        else if (URB_Status == URB_ERROR)
        {
            phost->Control.state = CTRL_ERROR;      
        }
        break;

    case CTRL_ERROR:
        /* 
        After a halt condition is encountered or an error is detected by the 
        host, a control endpoint is allowed to recover by accepting the next Setup 
        PID; i.e., recovery actions via some other pipe are not required for control
        endpoints. For the Default Control Pipe, a device reset will ultimately be 
        required to clear the halt or error condition if the next Setup PID is not 
        accepted.
        */
        if (++ phost->Control.errorcount <= USBH_MAX_ERROR_COUNT)
        {
            /* Do the transmission again, starting from SETUP Packet */
            phost->Control.state = CTRL_SETUP; 
        }
        else
        {
            phost->Control.status = CTRL_FAIL;
            phost->gState = phost->gStateBkp;

            status = USBH_FAIL;
        }
        break;

    default:
        break;
    }
    
    return status;
}


/**
* @}
*/ 

/************************ (C) COPYRIGHT 2014 Megahuntmicro ****END OF FILE****/
